package gotlib

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/gogf/gf/v2/crypto/gmd5"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gcharset"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/encoding/gurl"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
)

// EncryptPassword 密码加密
func EncryptPassword(password, salt string) string {
	return gmd5.MustEncryptString(gmd5.MustEncryptString(password) + gmd5.MustEncryptString(salt))
}

// TimeStampToDateTime 时间戳转 yyyy-MM-dd HH:mm:ss
func TimeStampToDateTime(timeStamp int64) string {
	tm := gtime.NewFromTimeStamp(timeStamp)
	return tm.Format("Y-m-d H:i:s")
}

// TimeStampToDate 时间戳转 yyyy-MM-dd
func TimeStampToDate(timeStamp int64) string {
	tm := gtime.NewFromTimeStamp(timeStamp)
	return tm.Format("Y-m-d")
}

// GetDomain 获取当前请求接口域名
func GetDomain(r *ghttp.Request) (string, error) {
	pathInfo, err := gurl.ParseURL(r.GetUrl(), -1)
	if err != nil {
		g.Log().Error(r.GetCtx(), err)
		err = gerror.New("解析附件路径失败")
		return "", err
	}
	return fmt.Sprintf("%s://%s:%s/", pathInfo["scheme"], pathInfo["host"], pathInfo["port"]), nil
}

// GetClientIp 获取客户端IP
func GetClientIp(r *ghttp.Request) string {
	ip := r.Header.Get("X-Forwarded-For")
	if ip == "" {
		ip = r.GetClientIp()
	}
	return ip
}

// GetLocalIP 服务端ip
func GetLocalIP() (ip string, err error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return
	}
	for _, addr := range addrs {
		ipAddr, ok := addr.(*net.IPNet)
		if !ok {
			continue
		}
		if ipAddr.IP.IsLoopback() {
			continue
		}
		if !ipAddr.IP.IsGlobalUnicast() {
			continue
		}
		return ipAddr.IP.String(), nil
	}
	return
}

// GetCityByIp 获取ip所属城市
func GetCityByIp(ip string) string {
	if ip == "" {
		return ""
	}
	if ip == "[::1]" || ip == "127.0.0.1" {
		return "内网IP"
	}
	url := "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip
	bytes := g.Client().GetBytes(context.Background(), url)
	src := string(bytes)
	srcCharset := "GBK"
	tmp, _ := gcharset.ToUTF8(srcCharset, src)
	json, err := gjson.DecodeToJson(tmp)
	if err != nil {
		return ""
	}
	if json.Get("code").Int() == 0 {
		city := fmt.Sprintf("%s %s", json.Get("pro").String(), json.Get("city").String())
		return city
	} else {
		return ""
	}
}

// StrToTimestamp 日期字符串转时间戳（秒）
func StrToTimestamp(dateStr string) int64 {
	tm, err := gtime.StrToTime(dateStr)
	if err != nil {
		g.Log().Error(context.Background(), err)
		return 0
	}
	return tm.Timestamp()
}

// GetDbConfig get db config
func GetDbConfig() (cfg *gdb.ConfigNode, err error) {
	cfg = g.DB().GetConfig()
	err = ParseDSN(cfg)
	return
}

// ParseDSN parses the DSN string to a Config
func ParseDSN(cfg *gdb.ConfigNode) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = gerror.New(r.(string))
		}
	}()
	dsn := cfg.Link
	if dsn == "" {
		return
	}
	foundSlash := false
	// gose:123456@tcp(192.168.0.212:3306)/gose-v2
	for i := len(dsn) - 1; i >= 0; i-- {
		if dsn[i] == '/' {
			foundSlash = true
			var j, k int

			// left part is empty if i <= 0
			if i > 0 {
				// [username[:password]@][protocol[(address)]]
				// Find the last '@' in dsn[:i]
				for j = i; j >= 0; j-- {
					if dsn[j] == '@' {
						// username[:password]
						// Find the first ':' in dsn[:j]
						for k = 0; k < j; k++ {
							if dsn[k] == ':' {
								cfg.Pass = dsn[k+1 : j]
								cfg.User = dsn[:k]
								break
							}
						}
						break
					}
				}

				// gose:123456@tcp(192.168.0.212:3306)/gose-v2
				// [protocol[(address)]]
				// Find the first '(' in dsn[j+1:i]
				var h int
				for k = j + 1; k < i; k++ {
					if dsn[k] == '(' {
						// dsn[i-1] must be == ')' if an address is specified
						if dsn[i-1] != ')' {
							if strings.ContainsRune(dsn[k+1:i], ')') {
								panic("invalid DSN: did you forget to escape a param value?")
							}
							panic("invalid DSN: network address not terminated (missing closing brace)")
						}
						for h = k + 1; h < i-1; h++ {
							if dsn[h] == ':' {
								cfg.Host = dsn[k+1 : h]
								cfg.Port = dsn[h+1 : i-1]
								break
							}
						}
						break
					}
				}
			}
			for j = i + 1; j < len(dsn); j++ {
				if dsn[j] == '?' {
					cfg.Name = dsn[i+1 : j]
					break
				} else {
					cfg.Name = dsn[i+1:]
				}
			}
			break
		}
	}
	if !foundSlash && len(dsn) > 0 {
		panic("invalid DSN: missing the slash separating the database name")
	}
	return
}

// GetRealFilesUrl 获取附件真实路径
func GetRealFilesUrl(r *ghttp.Request, path string) (realPath string, err error) {
	if gstr.ContainsI(path, "http") {
		realPath = path
		return
	}
	realPath, err = GetDomain(r)
	if err != nil {
		return
	}
	realPath = realPath + path
	return
}

// GetFilesPath 获取附件相对路径
func GetFilesPath(fileUrl string) (path string, err error) {
	ctx := context.Background()
	upType := gstr.ToLower(g.Cfg().MustGet(ctx, "upload.type").String())
	upPath := gstr.Trim(g.Cfg().MustGet(ctx, "upload.local.UpPath").String(), "/")
	if upType != "local" || (upType == "local" && !gstr.ContainsI(fileUrl, upPath)) {
		path = fileUrl
		return
	}
	pathInfo, err := gurl.ParseURL(fileUrl, 32)
	if err != nil {
		g.Log().Error(ctx, err)
		err = gerror.New("解析附件路径失败")
		return
	}
	pos := gstr.PosI(pathInfo["path"], upPath)
	if pos >= 0 {
		path = gstr.SubStr(pathInfo["path"], pos)
	}
	return
}

// CurrencyLong 货币转化为分
func CurrencyLong(currency interface{}) int64 {
	strArr := gstr.Split(gconv.String(currency), ".")
	switch len(strArr) {
	case 1:
		return gconv.Int64(strArr[0]) * 100
	case 2:
		if len(strArr[1]) == 1 {
			strArr[1] += "0"
		} else if len(strArr[1]) > 2 {
			strArr[1] = gstr.SubStr(strArr[1], 0, 2)
		}
		return gconv.Int64(strArr[0])*100 + gconv.Int64(strArr[1])
	}
	return 0
}

func GetExcPath() string {
	file, _ := exec.LookPath(os.Args[0])
	// 获取包含可执行文件名称的路径
	path, _ := filepath.Abs(file)
	// 获取可执行文件所在目录
	index := strings.LastIndex(path, string(os.PathSeparator))
	ret := path[:index]
	return strings.Replace(ret, "\\", "/", -1)
}

// GetInterfaceType 获取interface类型名
func GetInterfaceType(object interface{}) reflect.Type {
	val := reflect.ValueOf(object)
	return reflect.Indirect(val).Type()
}

// MergeMapStrStr 合并多个map[string]string
func MergeMapStrStr(mObj ...map[string]string) map[string]string {
	newObj := map[string]string{}
	for _, m := range mObj {
		for k, v := range m {
			newObj[k] = v
		}
	}
	return newObj
}

// UniqueInt64 数组去重
func UniqueInt64(list []int64) []int64 {
	var x []int64 = []int64{}
	for _, i := range list {
		if len(x) == 0 {
			x = append(x, i)
		} else {
			for k, v := range x {
				if i == v {
					break
				}
				if k == len(x)-1 {
					x = append(x, i)
				}
			}
		}
	}
	return x
}

// ObjectToJson 对象转Json
func ObjectToJson(obj interface{}) (jsonString string) {
	xBytes, _ := gjson.Encode(obj)
	return gconv.String(xBytes)
}

// JsonToMapStrStr Json string转为MapStrStr。
func JsonToMapStrStr(jsonString string) g.MapStrStr {
	ret, _ := gjson.Decode(jsonString)
	return gconv.MapStrStr(ret)
}

// JsonToMap Json string转为Map。
func JsonToMap(jsonString string) g.Map {
	ret, _ := gjson.Decode(jsonString)
	return gconv.Map(ret)
}

// JsonToInt64s Json string转为为int数组。 json格式为 [122,323,444]
func JsonToInt64s(jsonString string) []int64 {
	ret, _ := gjson.Decode(jsonString)
	return gconv.Int64s(ret)
}
func JsonToUint64s(jsonString string) []uint64 {
	ret, _ := gjson.Decode(jsonString)
	return gconv.Uint64s(ret)
}

// JsonToInts Json string转为为int数组。 json格式为 [122,323,444]
func JsonToInts(jsonString string) []int {
	ret, _ := gjson.Decode(jsonString)
	return gconv.Ints(ret)
}

func StringToInt64s(str string) (ret []int64) {
	temp := strings.Split(str, ",")
	for _, item := range temp {
		txt := strings.TrimSpace(item)
		if txt != "" {
			ret = append(ret, gconv.Int64(txt))
		}
	}
	return
}

// GetStructFieldString 获取Struct某个fieldName字段的String
func GetStructFieldString(structObj interface{}, fieldName string) (value string, err error) {
	rv := reflect.ValueOf(structObj)
	elem := rv.Elem()
	if elem.Kind() == reflect.Struct {
		ret := elem.FieldByName(fieldName)
		if ret.Kind() == reflect.String {
			return ret.String(), err
		} else {
			return ret.String(), gerror.New("getStructFieldString字段不是String类型")
		}
	}
	return "", gerror.New("getStructFieldString不是Struct类型")
}

// WFNumToStepNo 根据数字得出StepNo
func WFNumToStepNo(iStepNo int) (stepNo string) {
	if iStepNo < 10 {
		return strings.Join([]string{"Step0", gconv.String(iStepNo)}, "")
	}
	return strings.Join([]string{"Step", gconv.String(iStepNo)}, "")
}

// GetValueByJsonKey 从Json字符串中获取Key对应的Value
func GetValueByJsonKey(selectData string, key string) string {
	var data g.MapStrStr
	err := json.Unmarshal(gconv.Bytes(selectData), &data)
	if err != nil {
		return ""
	}
	return data[key]
}

// InInt64s 在Int64数组中包含某个数字
func InInt64s(item int64, items []int64) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

// StructCopySame 赋值结构体里相同字段到新结构体中； 调用： StructCopySame(&fromStruct, &toStruct) //todo 有问题
func StructCopySame(fromStruct interface{}, toStruct interface{}) {
	bVal := reflect.ValueOf(toStruct).Elem()   //获取reflect.Type类型
	vVal := reflect.ValueOf(fromStruct).Elem() //获取reflect.Type类型
	vTypeOfT := vVal.Type()
	iNum := vVal.NumField()

	for i := 0; i < iNum; i++ {
		// 在要修改的结构体中查询有数据结构体中相同属性的字段，有则修改其值
		name := vTypeOfT.Field(i).Name
		if ok := bVal.FieldByName(name).IsValid(); ok {
			bVal.FieldByName(name).Set(reflect.ValueOf(vVal.Field(i).Interface()))
		}
	}
}

// func ReplaceInt64ItemInJsonString(sourceJsonString string, from, to int64) (newJsonString string) {
// 	items := JsonToInt64s(sourceJsonString)

// 	iLen := len(items)
// 	for i := 0; i < iLen; i++ {
// 		if items[i] == from {
// 			items[i] = to
// 		}
// 	}
// 	json, _ := json.Marshal(items)
// 	return gconv.String(json)
// }

func DeleteElementFromArrayString(list []string, ele string) []string {
	result := make([]string, 0)
	for _, v := range list {
		if v != ele {
			result = append(result, v)
		}
	}
	return result
}
